package com.bawei.http

import android.os.Environment
import com.alibaba.android.arouter.launcher.ARouter
import com.bawei.base.constants.BaseConst
import com.bawei.base.path.RoutePath
import com.tencent.mmkv.MMKV
import okhttp3.Cache
import okhttp3.Interceptor
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Protocol
import okhttp3.Response
import okhttp3.ResponseBody.Companion.toResponseBody
import okhttp3.logging.HttpLoggingInterceptor
import org.json.JSONObject
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.io.File
import java.net.Proxy
import java.security.SecureRandom
import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLHandshakeException
import javax.net.ssl.SSLSocketFactory
import javax.net.ssl.X509TrustManager

/**
 * Author: Jason Zhang
 * Date: 2025/4/23
 * Desc: 网络封装
 *
 * OKHttp的基本原理
 * 1.底层基于HttpURLConnection实现
 * 2.使用线程池管理所有请求连接（连接池），默认最大可以开启64连接
 * 3.请求和返回处理时采用责任链的设计模式（拦截器）
 * 4.采用调度器管理请求
 *
 * 以下封装存在的问题：
 * 1.OKHttpClient和Retrofit存在多份实例
 * 2.会开启多个线程池，导致内存浪费
 * 3.连接请求没有复用，导致线程浪费
 * 4.存在单个连接时间过长
 * 5.没有对请求进行统一处理，如关闭请求
 *
 */
object HttpManager {

    /**
     * 获取OkHttpClient
     */

    private fun getOkHttpClient(): OkHttpClient {
        var okBuilder = OkHttpClient.Builder()
        okBuilder.apply {
            //设置连接超时时间
            connectTimeout(BaseConst.CONNECT_TIME_OUT, TimeUnit.SECONDS)
            //设置读取超时时间
            readTimeout(BaseConst.READ_TIME_OUT, TimeUnit.SECONDS)
            //设置写入超时时间
            writeTimeout(BaseConst.WRITE_TIME_OUT, TimeUnit.SECONDS)

            //设置接口请求日志拦截器
            if (BuildConfig.isOpenLog){
                addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            }

            //Header参数拦截器
            addInterceptor(getHeaderInterceptor())

            //服务器异常拦截器
            addInterceptor(getServiceExceptionInterceptor())

            //添加Https支持
            sslSocketFactory(sslSocketFactory, trustManager)

            //防止抓包软件获取接口数据
            proxy(Proxy.NO_PROXY)

            //设置支持的网络协议
            protocols(listOf(Protocol.HTTP_2))

            //设置缓存目录
            cache(Cache(File(""), 10 * 1024 * 1024))
        }

        return  okBuilder.build()

    }

    /**
     * 对称加密与非对称加密的区别
     * 1.对称加密：加密和解密使用同一个串，非对称加密的加密串分为公钥和私钥，私钥一般放在服务端，公钥属于公开密钥串
     * 2.非对称加密流程：
     *   场景一：服务端 ---> 客户端发送数据   服务端使用私钥串加密数据    ---->  客户端使用公钥串解密数据
     *   场景二：客户端 ---> 服务端发送数据   客户端使用公钥串加密数据    ---->  服务端使用私钥串解密数据
     * 3.对称加密流程：
     *   场景一：服务端 ---> 客户端发送数据   服务端使用公钥串加密数据    ---->  客户端使用公钥串解密数据
     *   场景二：客户端 ---> 服务端发送数据   客户端使用公钥串加密数据    ---->  服务端使用公钥串解密数据
     */
    private val sslSocketFactory: SSLSocketFactory
        get() = try{
            val sslContext = SSLContext.getInstance("SSL")
            sslContext.init(null, arrayOf(trustManager), SecureRandom())
            sslContext.socketFactory
        } catch (e: Exception) {
            throw RuntimeException(e)
        }

    private val trustManager: X509TrustManager
        get() = object : X509TrustManager {
            /**
             * 检查客户端的证书
             */
            override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) { }

            /**
             * 检查服务端的证书
             */
            override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {
                //防止抓包工具，如Fiddler, Charles抓取数据
                chain?.forEach {certificate ->
                    if (certificate.issuerX500Principal.name.contains("Charles")||
                        certificate.issuerX500Principal.name.contains("Fiddler")){
                        throw SSLHandshakeException("Fiddler或Charles证书被拒绝")
                    }
                }
            }

            override fun getAcceptedIssuers(): Array<X509Certificate> {
               return arrayOf()
            }
        }

    /**
     * 获取Retrofit
     */
    private fun getRetrofit(): Retrofit {
        var retrofitBuilder = Retrofit.Builder()
        //apply扩展函数用于对象赋值，返回当前对象
        retrofitBuilder.apply {
            baseUrl(BuildConfig.BASE_URL)
            client(getOkHttpClient())
            addConverterFactory(GsonConverterFactory.create())
        }
        return retrofitBuilder.build()
    }

    /**
     * 请求头拦截器
     */
    private fun getHeaderInterceptor(): Interceptor {
        return object : Interceptor {
            override fun intercept(chain: Interceptor.Chain): Response {
                //获取Token
                var token = MMKV.defaultMMKV().decodeString("token", "park_2208")
                var newBuilder = chain.request().newBuilder()
                if (token != null) {
                    newBuilder.header("token", token).build()
                }
                //获取AccessId
                var accessId = MMKV.defaultMMKV().decodeString("accessId", "")
                if (accessId != null) {
                    newBuilder.header("accessId", accessId).build()
                }
                //执行请求
                var newRequest = newBuilder.build()
                var response = chain.proceed(newRequest)

                //验证Token是否过期
                if (response.code == 403 || response.code == 401) {
                    ARouter.getInstance().build(RoutePath.PATH_LOGIN_PAGE).navigation()
                }

                return response
            }
        }
    }

    /**
     * 服务器异常拦截器
     * Interceptor属于责任链模式
     */
    private fun getServiceExceptionInterceptor(): Interceptor{
        return object : Interceptor {
            override fun intercept(chain: Interceptor.Chain): Response {
                var errorResponse = chain.proceed(chain.request())
                var responseBuilder = errorResponse.newBuilder()
                if(errorResponse.code == 500){
                    var newJson = JSONObject()
                    newJson.put("code", 500)
                    newJson.put("message", "服务器异常, 请联系客服！")
                    responseBuilder.body(newJson.toString().toResponseBody("application/json;charset=UTF-8".toMediaTypeOrNull()))
                }
                return responseBuilder.build()
            }
        }

    }

    /**
     * 获取ApiServer实例对象
     */
    fun <T> getApiServer(clazz: Class<*>): T {
        return getRetrofit().create(clazz) as T
    }

}